#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <string.h>
#include <stdint.h>

struct fingerprint {
	char fingerprint_name[100];
	char fingerprint[100];
	struct bpf_program *fp;
	struct fingerprint* next;
	uint64_t count;
	uint64_t bytes;
};

struct fingerprint* fingerprints = NULL;
char errbuf[PCAP_ERRBUF_SIZE];
static pcap_t* pPCAPDescriptor;
static uint64_t numPackets = 0;

//------------------------------------------

int sniffer_init(char* pDevice)
{

	char errbuf[PCAP_ERRBUF_SIZE];
	
	if (pDevice == NULL) {
		fprintf(stderr, "Couldn't find device\n");
		return 2;
	}
	
	printf("Device: %s\n", pDevice);
	
	/* open device for reading */
	
	pPCAPDescriptor = pcap_open_live(pDevice, BUFSIZ, 0, -1, errbuf);
	
	if(pPCAPDescriptor == NULL)
	{
		printf("pcap_open_live(): %s\n", errbuf);
		return 3;
	}

	return 0;

}

//------------------------------------------

int read_fingerprints()
{

	fprintf(stdout, "Loading BPF Fingerprints...\n");

	char fingerprint_name[100];
	char fingerprint_bpf[100];
	
	FILE *f = fopen("bpf.dict", "r");
	
	if (f == NULL)
	{
		fprintf(stderr, "Unable to open file\n");
		return(2);
	}
	
	#define LINESZ 1024
	char buff[LINESZ];
	struct fingerprint** pFingerprints = &fingerprints;

	while (fgets(buff, LINESZ, f))
	{
	
		// was it a comment?
		
		if (buff[0] != '#')
		{
	
			if (sscanf( buff, "%s %[^\t\n]",  fingerprint_name, fingerprint_bpf) == 2)
			{
				fprintf(stdout, "Loaded '%s' = '%s'\n", fingerprint_name, fingerprint_bpf);

				*pFingerprints = (struct fingerprint*) calloc(1, sizeof(struct fingerprint));

				(*pFingerprints)->fp = (struct bpf_program*) calloc(1, sizeof(struct bpf_program));

				strcpy((*pFingerprints)->fingerprint_name, fingerprint_name);
				strcpy((*pFingerprints)->fingerprint, fingerprint_bpf);

				if (pcap_compile(pPCAPDescriptor, (*pFingerprints)->fp, fingerprint_bpf, 0, 0) != 0) // zero on success
				{
					fprintf(stderr, "Unable to compile filgerprint: [%s] %s\n", fingerprint_name, fingerprint_bpf);
					return 3;
				}

				pFingerprints = &((*pFingerprints)->next);
			}
		
		}
		
	}

	fclose(f);
	
	return 0;

}

//------------------------------------------

void print_stats(void)
{

	struct fingerprint* pFingerprints = fingerprints;

	fprintf(stdout, "# ################################\n");
	fprintf(stdout, "#                  fingerprint,    packet count,           bytes\n");

	while (pFingerprints != NULL)
	{
	
		fprintf(stdout, "%30s, %15llu, %15llu\n", pFingerprints->fingerprint_name, pFingerprints->count, pFingerprints->bytes);
	
		pFingerprints = pFingerprints->next;
	}

	fprintf(stdout, "\n");
	
	fflush(stdout);
}


//------------------------------------------

void process_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
{

	numPackets++;
	
	if ( numPackets % 1000 == 0 )
	{
		print_stats();
	}
	
	struct fingerprint* pFingerprints = fingerprints;

	while (pFingerprints != NULL)
	{
	
		if (pcap_offline_filter(pFingerprints->fp, h, bytes) != 0)
		{
			pFingerprints->count++;
			pFingerprints->bytes+=h->len;
		}
	
		pFingerprints = pFingerprints->next;
	}
	
}

//------------------------------------------

int main(int argc, char* argv[])
{

	if(argc < 2)
	{
		fprintf(stdout, "Usage: %s interface\n", argv[0]);
		fprintf(stdout, "\tinterface = i.e. eth0, or wlan0\n");
		return 0;
	}
	
	if (sniffer_init(argv[1]) == 0)
	{
		
		
		if (read_fingerprints() == 0)
		{
		
			pcap_loop(pPCAPDescriptor, 0, process_packet, NULL);
			
		}
		
	}
	return 0;
}

//------------------------------------------
